/*
 * Copyright [2009] [Marcin Rzeźnicki]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 */
package mr.go.yaxc;

import java.util.HashMap;
import java.util.Map;

/**
 * <code>ActionInfo</code> serves as configuration object and "factory" to
 * {@link Action}. For every action type there is only one instance of
 * <code>ActionInfo</code> - the one created during registration. Users can
 * configure various <code>Action</code> aspects using instances of this class,
 * which they typically obtain using one of methods of {@link Actions}. This
 * class exists to provide seperation between <code>Action's</code> behaviour and its
 * configuration. Implementors of <code>Action</code> class can use
 * <code>ActionInfo</code> for other purposes: customized creation or
 * parametrization of their subclass. If <code>ActionInfo</code> subclass can be
 * found in the same location as registered <code>Action</code> under name
 * <code>actionClass.getName() + "Info"</code>, it will be picked up by
 * registration mechanism. If no <code>ActionInfo</code> is provided, the
 * library will synthetize default implementation
 * 
 * @author Marcin Rzeźnicki
 * 
 */
public abstract class ActionInfo implements Comparable<ActionInfo> {

    private String attributePrefix = "";
    private Map<String, String> attributesMap;
    private int level;
    private LocationMatcher locationMatcher;
    private ActionMatch matchType;
    private String pattern;
    private final int priority;

    /**
     * Constructor
     *
     * @param matchType
     *            algorithm used for matching action to document location
     * @param pattern
     *            pattern used for matching action to document location
     * @param priority
     *            actions with higher priority (closer to zero) are matched
     *            before actions with lower priority
     * @throws IllegalArgumentException
     *             when <code>pattern == null</code> or
     *             <code>priority < 0 </code>
     */
    public ActionInfo(ActionMatch matchType, String pattern, int priority) {
        if (pattern == null) {
            throw new IllegalArgumentException("pattern == null");
        }
        if (priority < 0) {
            throw new IllegalArgumentException("priority < 0");
        }
        this.matchType = matchType;
        this.pattern = pattern;
        this.priority = priority;
    }

    /**
     * Constructor using {@link ActionMatch#ENDS_WITH} as default
     *
     * @param pattern
     *            pattern used for matching action to document location
     * @param priority
     *            actions with higher priority (closer to zero) are matched
     *            before actions with lower priority
     * @throws IllegalArgumentException
     *             when <code>pattern == null</code> or
     *             <code>priority < 0 </code>
     */
    public ActionInfo(String pattern, int priority) {
        this(ActionMatch.ENDS_WITH,
             pattern,
             priority);
    }

    @Override
    public int compareTo(ActionInfo o) {
        return priority - o.priority;
    }

    /**
     * Equality is determined by equality of {@link #getActionClass()}
     */
    @Override
    public final boolean equals(Object obj) {
        if (obj instanceof ActionInfo == false) {
            return false;
        }
        final ActionInfo other = (ActionInfo) obj;
        return other.getActionClass().equals(this.getActionClass());
    }

    /**
     * Returns type of action associated with this <code>ActionInfo</code>.
     * There is no need for this type to be the same as the type of object
     * returned by {@link #getImplementation()}, so you may use these methods to
     * provide specialized instantiation. Whatever type is returned by this
     * method it is unique among all registered actions. If
     * <code>ActionInfo</code> with the same type is registered, it will be
     * unregistered first during registration of this <code>ActionInfo</code>
     *
     * @return type of action
     */
    public abstract Class<? extends Action> getActionClass();

    /**
     *
     * @return namespace prefix of attributes examined by associated
     *         {@link Action} or empty string
     */
    public String getAttributePrefix() {
        return attributePrefix;
    }

    /**
     *
     * @return overriden attribute names or null
     */
    public Map<String, String> getAttributesMap() {
        return attributesMap;
    }

    /**
     * "Factory" method. Creates {@link Action} on behalf of parser. Every call
     * should return new instance of {@link Action}
     *
     * @return new instance of <code>Action</code>
     */
    public abstract Action getImplementation();

    /**
     * <code> level </code> used in conjunction with {@link ActionMatch#LEVEL}.
     * Defaults to 0
     *
     * @return level
     */
    public int getLevel() {
        return level;
    }

    /**
     * Custom {@link LocationMatcher} used in conjunction with
     * {@link ActionMatch#CUSTOM}
     *
     * @return location matcher
     */
    public LocationMatcher getLocationMatcher() {
        return locationMatcher;
    }

    /**
     * Algorithm used for matching action to document location
     *
     * @return match type
     */
    public ActionMatch getMatchType() {
        return matchType;
    }

    /**
     * Pattern used for matching action to document location. Defaults to empty
     * string
     *
     * @return pattern
     */
    public String getPattern() {
        return pattern;
    }

    /**
     * Priority of <code>Action</code>
     *
     * @return priority
     * @see Actions#PRIORITY_NORMAL
     */
    public int getPriority() {
        return priority;
    }

    @Override
    public final int hashCode() {
        return getActionClass().hashCode();
    }

    /**
     * Sets XML namespace attribute prefix. Defaults to empty string if
     * namespaces are not used, or attributes of parsed documents are not within
     * any XML namespace
     *
     * @param attributePrefix
     *            XML namespace attribute prefix
     * @throws IllegalArgumentException
     *             if <code>attributePrefix == null</code>
     */
    public void setAttributePrefix(String attributePrefix) {
        if (attributePrefix == null) {
            throw new IllegalArgumentException("attributePrefix == null");
        }
        this.attributePrefix = attributePrefix;
    }

    /**
     * Overrides attribute names
     *
     * @param attributesMap
     *            mapping of attribute names expected by <code>Action</code> to
     *            XML attribute names
     */
    public void setAttributesMap(Map<String, String> attributesMap) {
        this.attributesMap = attributesMap;
    }

    /**
     * Overrides attribute names
     *
     * @param attribute
     *            attribute name expected by <code>Action</code>
     * @param attributeName
     *            XML attribute name
     */
    public void setAttributesMap(String attribute, String attributeName) {
        if (attributesMap == null) {
            attributesMap = new HashMap<String, String>();
        }
        attributesMap.put(attribute,
                          attributeName);
    }

    /**
     * Sets level
     *
     * @param level
     * @see #getLevel()
     * @see ActionMatch#LEVEL
     */
    public void setLevel(int level) {
        this.level = level;
    }

    /**
     * Sets custom matcher
     *
     * @param locationMatcher
     * @see #getLocationMatcher()
     * @see ActionMatch#CUSTOM
     */
    public void setLocationMatcher(LocationMatcher locationMatcher) {
        this.locationMatcher = locationMatcher;
    }

    /**
     * Sets algorithm used for matching action to document location
     *
     * @param matchType
     * @see ActionMatch
     */
    public void setMatchType(ActionMatch matchType) {
        this.matchType = matchType;
    }

    /**
     * Sets pattern used for matching action to document location
     *
     * @param pattern
     * @see ActionMatch
     */
    public void setPattern(String pattern) {
        this.pattern = pattern;
    }
}
